package org.codefinger.dao;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.codefinger.dao.callback.BigDecimalQueryCallback;
import org.codefinger.dao.callback.BigIntegerQueryCallback;
import org.codefinger.dao.callback.BooleanQueryCallback;
import org.codefinger.dao.callback.ByteQueryCallback;
import org.codefinger.dao.callback.DateQueryCallback;
import org.codefinger.dao.callback.DoubleQueryCallback;
import org.codefinger.dao.callback.FloatQueryCallback;
import org.codefinger.dao.callback.IntQueryCallback;
import org.codefinger.dao.callback.LongQueryCallback;
import org.codefinger.dao.callback.MapListQueryCallback;
import org.codefinger.dao.callback.MapQueryCallback;
import org.codefinger.dao.callback.MapSetQueryCallback;
import org.codefinger.dao.callback.PojoListQueryCallback;
import org.codefinger.dao.callback.PojoQueryCallback;
import org.codefinger.dao.callback.PojoSetQueryCallback;
import org.codefinger.dao.callback.ResultQueryCallback;
import org.codefinger.dao.callback.ShortQueryCallback;
import org.codefinger.dao.callback.StringQueryCallback;
import org.codefinger.dao.impl.Querier;
import org.codefinger.dao.impl.QueryParam;
import org.codefinger.dao.util.AbstractCache.ValueBuilder;
import org.codefinger.dao.util.IdentityCache;

public class AbstractQuery {

	private IdentityCache<Class<?>, PojoQueryCallback<?>>		pojoCallCache;

	private IdentityCache<Class<?>, PojoListQueryCallback<?>>	pojoListCallCache;

	private IdentityCache<Class<?>, PojoSetQueryCallback<?>>	pojoSetCallCache;

	protected Querier											querier;

	protected QueryParam										param;

	public AbstractQuery(QueryParam param, Querier querier) {
		super();
		this.param = param;
		this.querier = querier;
		if (querier != null) {
			this.pojoCallCache = new IdentityCache<Class<?>, PojoQueryCallback<?>>(16, new PojoCallbackBuilder(querier.getAliases()));
			this.pojoListCallCache = new IdentityCache<Class<?>, PojoListQueryCallback<?>>(16, new PojoListCallbackBuilder(querier.getAliases()));
			this.pojoSetCallCache = new IdentityCache<Class<?>, PojoSetQueryCallback<?>>(16, new PojoSetCallbackBuilder(querier.getAliases()));
		}
	}

	@SuppressWarnings("unchecked")
	public <T> T fetch(Class<T> clas) {
		return (T) querier.executeQuery(param, pojoCallCache.get(clas));
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> getList(Class<T> clas) {
		return (List<T>) querier.executeQuery(param, pojoListCallCache.get(clas));
	}

	@SuppressWarnings("unchecked")
	public <T> Set<T> getSet(Class<T> clas) {
		return (Set<T>) querier.executeQuery(param, pojoSetCallCache.get(clas));
	}

	public Map<String, Object> fetch() {
		return querier.executeQuery(param, MapQueryCallback.INSTANCE);
	}

	public <T> List<Map<String, Object>> getMapList() {
		return querier.executeQuery(param, MapListQueryCallback.INSTANCE);
	}

	public <T> Set<Map<String, Object>> getMapSet() {
		return querier.executeQuery(param, MapSetQueryCallback.INSTANCE);
	}

	@SuppressWarnings("unchecked")
	public <T> T getUnique(DaoType<T> type) {
		switch (type.getType()) {
		case 0:
			return (T) querier.executeQuery(param, IntQueryCallback.INSTANCE);
		case 1:
			return (T) querier.executeQuery(param, DoubleQueryCallback.INSTANCE);
		case 2:
			return (T) querier.executeQuery(param, FloatQueryCallback.INSTANCE);
		case 3:
			return (T) querier.executeQuery(param, LongQueryCallback.INSTANCE);
		case 4:
			return (T) querier.executeQuery(param, ShortQueryCallback.INSTANCE);
		case 5:
			return (T) querier.executeQuery(param, ByteQueryCallback.INSTANCE);
		case 6:
			return (T) querier.executeQuery(param, BooleanQueryCallback.INSTANCE);
		case 8:
			return (T) querier.executeQuery(param, BigDecimalQueryCallback.INSTANCE);
		case 9:
			return (T) querier.executeQuery(param, BigIntegerQueryCallback.INSTANCE);
		case 10:
			return (T) querier.executeQuery(param, DateQueryCallback.INSTANCE);
		default:
			return (T) querier.executeQuery(param, StringQueryCallback.INSTANCE);
		}
	}

	public <T> T getResult(QueryCallback<T> queryCallback) {
		return querier.executeQuery(param, //
				new ResultQueryCallback<T>(queryCallback));
	}

	public int queryCount() {
		return querier.queryCount(param);
	}

	private static class PojoCallbackBuilder implements ValueBuilder<Class<?>, PojoQueryCallback<?>> {

		private String[]	aliases;

		public PojoCallbackBuilder(String[] aliases) {
			super();
			this.aliases = aliases;
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		@Override
		public PojoQueryCallback<?> build(Class<?> key) {
			return new PojoQueryCallback(key, aliases);
		}

	}

	private static class PojoListCallbackBuilder implements ValueBuilder<Class<?>, PojoListQueryCallback<?>> {

		private String[]	aliases;

		public PojoListCallbackBuilder(String[] aliases) {
			super();
			this.aliases = aliases;
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		@Override
		public PojoListQueryCallback<?> build(Class<?> key) {
			return new PojoListQueryCallback(key, aliases);
		}

	}

	private static class PojoSetCallbackBuilder implements ValueBuilder<Class<?>, PojoSetQueryCallback<?>> {

		private String[]	aliases;

		public PojoSetCallbackBuilder(String[] aliases) {
			super();
			this.aliases = aliases;
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		@Override
		public PojoSetQueryCallback<?> build(Class<?> key) {
			return new PojoSetQueryCallback(key, aliases);
		}

	}

}
